package com.luck.picture.lib.recordvideo;

import android.animation.ObjectAnimator;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.luck.picture.lib.R;
import com.luck.picture.lib.recordvideo.util.ToastUtils;
import com.qiniu.pili.droid.shortvideo.PLAudioEncodeSetting;
import com.qiniu.pili.droid.shortvideo.PLCameraSetting;
import com.qiniu.pili.droid.shortvideo.PLFocusListener;
import com.qiniu.pili.droid.shortvideo.PLMicrophoneSetting;
import com.qiniu.pili.droid.shortvideo.PLRecordSetting;
import com.qiniu.pili.droid.shortvideo.PLRecordStateListener;
import com.qiniu.pili.droid.shortvideo.PLShortVideoRecorder;
import com.qiniu.pili.droid.shortvideo.PLVideoEncodeSetting;
import com.qiniu.pili.droid.shortvideo.PLVideoSaveListener;

import java.text.SimpleDateFormat;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.luck.picture.lib.recordvideo.util.Config;
import com.luck.picture.lib.recordvideo.util.RecordSettings;

public class RecordVideoPage extends AppCompatActivity implements PLFocusListener,PLRecordStateListener,PLVideoSaveListener {
    public static final String EXTRA_KEY_OUTPUT_VIDEO_PATH = "com.aiyige.extra.EXTRA_KEY_OUTPUT_VIDEO_PATH";
    PLShortVideoRecorder shortVideoRecorder;
    PLCameraSetting plCameraSetting;
    PLMicrophoneSetting plMicrophoneSetting;
    PLVideoEncodeSetting plVideoEncodeSetting;
    PLAudioEncodeSetting plAudioEncodeSetting;
    PLRecordSetting plRecordSetting;
    int previewSizeRatio = 1;
    int previewSizeLevel = 5;
    int encodingMode = 0;
    int encodingSizeLevel = 16;
    int encodingBitrateLevel = 7;

    int recordSectionNum = 0;
    long recordedDuration = 0;
    long startRecordTimestamp = 0;
    boolean recording = false;
    boolean reachMaxRecordLimit = false;
    boolean enableFlash = false;

    GLSurfaceView preview;
    UpdateProgressThread updateProgressThread;
    ImageView finishBtn;
    ImageView rotateBtn;
    ImageView flashBtn;
    ImageView switchCameraBtn;
    ImageView discardBtn;
    ImageView completeBtn;
    TextView shotDurationTv;
    ImageView recordBtn;
    ProgressBar progressBar;
    CustomProgressDialog customProgressDialog;
    SimpleDateFormat recordDutationFormat = new SimpleDateFormat("mm : ss");
    String outputVideoPath;
    View controlPanel;
    boolean recorderReady;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.page_record_video);
        recorderReady = false;
        initView();
        outputVideoPath = getIntent().getStringExtra(EXTRA_KEY_OUTPUT_VIDEO_PATH);
        customProgressDialog = new CustomProgressDialog(this);
        customProgressDialog.setCancelable(false);
        customProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                shortVideoRecorder.cancelConcat();
            }
        });
        shortVideoRecorder = new PLShortVideoRecorder();
        shortVideoRecorder.setFocusListener(this);
        shortVideoRecorder.setRecordStateListener(this);
        plCameraSetting = new PLCameraSetting();
        plCameraSetting.setCameraId(chooseCameraFacingId());
        plCameraSetting.setCameraPreviewSizeRatio(getPreviewSizeRatio(previewSizeRatio));
        plCameraSetting.setCameraPreviewSizeLevel(getPreviewSizeLevel(previewSizeLevel));

        plMicrophoneSetting = new PLMicrophoneSetting();

        plVideoEncodeSetting = new PLVideoEncodeSetting(this);
        plVideoEncodeSetting.setEncodingSizeLevel(getEncodingSizeLevel(encodingSizeLevel));
        plVideoEncodeSetting.setEncodingBitrate(getEncodingBitrateLevel(encodingBitrateLevel));
        plVideoEncodeSetting.setHWCodecEnabled(encodingMode == 0);

        plAudioEncodeSetting = new PLAudioEncodeSetting();
        plAudioEncodeSetting.setHWCodecEnabled(encodingMode == 0);

        plRecordSetting = new PLRecordSetting();
        plRecordSetting.setMaxRecordDuration(RecordSettings.DEFAULT_MAX_RECORD_DURATION);
        plRecordSetting.setVideoCacheDir(Config.VIDEO_STORAGE_DIR);
        plRecordSetting.setVideoFilepath(TextUtils.isEmpty(outputVideoPath) ? Config.VIDEO_STORAGE_DIR + UUID.randomUUID().toString().replace("-", "") + ".mp4" : outputVideoPath);
        shortVideoRecorder.prepare(preview, plCameraSetting, plMicrophoneSetting, plVideoEncodeSetting, plAudioEncodeSetting, null, plRecordSetting);
        updateView();
        updateProgressThread = new UpdateProgressThread();
        updateProgressThread.start();
        resetProgress();
    }

    @Override
    protected void onDestroy() {
        shortVideoRecorder.destroy();
        updateProgressThread.destroyWork();
        super.onDestroy();
    }


    public void updateView() {
        updateRotateBtn();
        updateRecordBtn();
        updateCompleteBtn();
        updateDiscardBtn();
        updateFlashBtn();
        updateSwitchCameraBtn();
    }

    public void updateRotateBtn() {
        if (recording || recordSectionNum > 0) {
            rotateBtn.setVisibility(View.INVISIBLE);
        } else {
            rotateBtn.setVisibility(View.VISIBLE);
        }
    }

    public void updateSwitchCameraBtn() {
        if (recording) {
            switchCameraBtn.setVisibility(View.INVISIBLE);
        } else {
            switchCameraBtn.setVisibility(View.VISIBLE);
        }
    }

    public void discard() {
        if (recordSectionNum == 0) return;
        for (int i = 0; i < recordSectionNum; ++i) {
            shortVideoRecorder.deleteLastSection();
        }
        recordSectionNum = 0;
        recordedDuration = 0;
        startRecordTimestamp = 0;
        reachMaxRecordLimit = false;
    }

    public void updateRecordBtn() {
        if (reachMaxRecordLimit) {
            recordBtn.setVisibility(View.INVISIBLE);
            return;
        } else {
            recordBtn.setVisibility(View.VISIBLE);
        }
        if (recording) {
            recordBtn.setImageResource(R.drawable.shot_suspend);
        } else {
            recordBtn.setImageResource(R.drawable.shot_play);
        }
    }

    public void updateCompleteBtn() {
        if (recording) {
            completeBtn.setVisibility(View.INVISIBLE);
        } else {
            completeBtn.setVisibility(View.VISIBLE);
        }
    }

    public long calculateTotalRecordDuration() {
        if (recording) {
            return recordedDuration + System.currentTimeMillis() - startRecordTimestamp;
        } else {
            return recordedDuration;
        }
    }

    public void updateDiscardBtn() {
        if (!recording) {
            discardBtn.setVisibility(View.VISIBLE);
        } else {
            discardBtn.setVisibility(View.INVISIBLE);
        }
    }

    public void updateFlashBtn() {
        if (enableFlash) {
            flashBtn.setImageResource(R.drawable.shot_flash);
        } else {
            flashBtn.setImageResource(R.drawable.shot_flash_n);
        }

    }

    public void startRecord() {
        startRecordTimestamp = System.currentTimeMillis();
        recording = true;
        updateProgressThread.resumeWork();
        shortVideoRecorder.beginSection();
    }

    public void stopRecord() {
        shortVideoRecorder.endSection();
        recording = false;
        recordedDuration += System.currentTimeMillis() - startRecordTimestamp;
        ++recordSectionNum;
        updateProgressThread.pauseWork();
    }

    @Override
    protected void onResume() {
        super.onResume();
        shortVideoRecorder.resume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        shortVideoRecorder.pause();
    }

    @Override
    public void onManualFocusStart(boolean b) {

    }

    @Override
    public void onManualFocusStop(boolean b) {

    }

    @Override
    public void onManualFocusCancel() {

    }

    @Override
    public void onAutoFocusStart() {

    }

    @Override
    public void onAutoFocusStop() {

    }

    @Override
    public void onReady() {
        recorderReady = true;
    }

    @Override
    public void onError(int i) {
        Toast.makeText(this, R.string.record_video_failed, Toast.LENGTH_SHORT).show();
        finish();
    }

    @Override
    public void onDurationTooShort() {

    }

    @Override
    public void onRecordStarted() {

    }

    @Override
    public void onRecordStopped() {

    }

    @Override
    public void onSectionIncreased(long l, long l1, int i) {

    }

    @Override
    public void onSectionDecreased(long l, long l1, int i) {

    }

    @Override
    public void onRecordCompleted() {
        recordReachMaxLimit();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(RecordVideoPage.this, R.string.record_video_reach_max_duration, Toast.LENGTH_SHORT).show();
                updateView();
                updateProgress();
            }
        });
    }

    private PLCameraSetting.CAMERA_FACING_ID chooseCameraFacingId() {
        if (PLCameraSetting.hasCameraFacing(PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_BACK)) {
            return PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_BACK;
        } else {
            return PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_FRONT;
        }
    }

    private PLCameraSetting.CAMERA_PREVIEW_SIZE_RATIO getPreviewSizeRatio(int position) {
        return RecordSettings.PREVIEW_SIZE_RATIO_ARRAY[position];
    }

    private PLCameraSetting.CAMERA_PREVIEW_SIZE_LEVEL getPreviewSizeLevel(int position) {
        return RecordSettings.PREVIEW_SIZE_LEVEL_ARRAY[position];
    }

    private PLVideoEncodeSetting.VIDEO_ENCODING_SIZE_LEVEL getEncodingSizeLevel(int position) {
        return RecordSettings.ENCODING_SIZE_LEVEL_ARRAY[position];
    }

    private int getEncodingBitrateLevel(int position) {
        return RecordSettings.ENCODING_BITRATE_LEVEL_ARRAY[position];
    }

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
    }

    public void resetProgress() {
        progressBar.setProgress(0);
        shotDurationTv.setText("00 : 00");
    }

    public void updateProgress() {
        progressBar.setProgress((int) (progressBar.getMax() * (float) calculateTotalRecordDuration() / RecordSettings.DEFAULT_MAX_RECORD_DURATION));
        shotDurationTv.setText(recordDutationFormat.format(calculateTotalRecordDuration()));
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        resetProgress();
    }


    public void initView() {
        controlPanel = findViewById(R.id.controlPanel);
        preview = (GLSurfaceView) findViewById(R.id.preview);
        finishBtn = (ImageView) findViewById(R.id.finishBtn);
        rotateBtn = (ImageView) findViewById(R.id.rotateBtn);
        flashBtn = (ImageView) findViewById(R.id.flashBtn);
        switchCameraBtn = (ImageView) findViewById(R.id.switchCameraBtn);
        discardBtn = (ImageView) findViewById(R.id.discardBtn);
        completeBtn = (ImageView) findViewById(R.id.completeBtn);
        recordBtn = (ImageView) findViewById(R.id.recordBtn);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        shotDurationTv = (TextView) findViewById(R.id.shotDurationTv);
        finishBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                setResult(RESULT_CANCELED);
                finish();
            }
        });
        rotateBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                switch (getResources().getConfiguration().orientation) {
                    case Configuration.ORIENTATION_LANDSCAPE:
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                        break;
                    case Configuration.ORIENTATION_PORTRAIT:
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                        break;
                }
            }
        });

        flashBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                enableFlash = !enableFlash;
                shortVideoRecorder.setFlashEnabled(enableFlash);
                updateView();
            }
        });
        switchCameraBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                shortVideoRecorder.switchCamera();
            }
        });
        discardBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                discard();
                resetProgress();
                updateView();
            }
        });

        completeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                if (calculateTotalRecordDuration() >= RecordSettings.DEFAULT_MIN_RECORD_DURATION) {
                    concatVideo();
                } else {
                    Toast.makeText(RecordVideoPage.this, R.string.record_video_too_short, Toast.LENGTH_SHORT).show();
                }
            }
        });
        recordBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!recorderReady)return;
                if (recording) {
                    stopRecord();
                } else {
                    startRecord();
                }
                updateView();
            }
        });
    }




    public void concatVideo() {
        customProgressDialog.show();
        shortVideoRecorder.concatSections(this);
    }

    public void recordReachMaxLimit() {
        shortVideoRecorder.endSection();
        reachMaxRecordLimit = true;
        recording = false;
        recordedDuration += System.currentTimeMillis() - startRecordTimestamp;
        ++recordSectionNum;
        updateProgressThread.pauseWork();
    }

    @Override
    public void onSaveVideoSuccess(String s) {
        customProgressDialog.dismiss();
        setResult(RESULT_OK);
        finish();
    }

    @Override
    public void onSaveVideoFailed(int i) {
        customProgressDialog.dismiss();
        Toast.makeText(this, R.string.record_video_failed, Toast.LENGTH_SHORT).show();
        setResult(RESULT_CANCELED);
        finish();
    }

    @Override
    public void onSaveVideoCanceled() {
        customProgressDialog.dismiss();
        setResult(RESULT_CANCELED);
        finish();
    }

    @Override
    public void onProgressUpdate(float v) {
        customProgressDialog.setProgress(v);
    }



    public class UpdateProgressThread extends Thread {
        volatile boolean pause;
        volatile boolean stop;
        public static final long UPDATE_PROGERSS_INTERVAL_MS = 100;
        Lock mutex;
        Condition waitConditon;
        Handler mainHandler;

        public UpdateProgressThread() {
            pause = true;
            stop = false;
            mutex = new ReentrantLock();
            waitConditon = mutex.newCondition();
            mainHandler = new Handler(Looper.getMainLooper());
        }

        public void doUpdateProgress() {
            mainHandler.post(new Runnable() {
                @Override
                public void run() {
                    updateProgress();
                }
            });
        }

        @Override
        public void run() {
            while (true) {
                mutex.lock();
                if (stop) {
                    mutex.unlock();
                    return;
                }
                try {
                    if (pause) {
                        waitConditon.await();
                    } else {
                        doUpdateProgress();
                        waitConditon.await(UPDATE_PROGERSS_INTERVAL_MS, TimeUnit.MILLISECONDS);
                    }
                } catch (Exception e) {
                    try {
                        mutex.unlock();
                    } catch (Exception ex) {
                    } finally {
                        return;
                    }
                }
                mutex.unlock();
            }
        }

        public void pauseWork() {
            mutex.lock();
            pause = true;
            mutex.unlock();
        }

        public void resumeWork() {
            mutex.lock();
            pause = false;
            waitConditon.signalAll();
            mutex.unlock();
        }

        public void destroyWork() {
            mutex.lock();
            stop = true;
            pause = false;
            waitConditon.signalAll();
            mutex.unlock();
            try {
                join();
            } catch (Exception e) {
            }
        }


    }
}
